﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Web.Http;
using System.Web.Http.Controllers;
using Newtonsoft.Json.Linq;
using Sustainalytics.Entities;

namespace Sustainalytics.Claims
{
    /// <summary>
    /// How to use
    ///     [ClaimsCheckerAttribute(typeof(CLASS_IS_CHECKING))] 
    ///     ex. [ClaimsCheckerAttribute(typeof(CriteriaLevel1Controller))]
    /// </summary>
    public class ClaimsCheckerAttribute : AuthorizeAttribute
    {
        private readonly string _resource;
        private readonly string[] _restVerbs= new string[] { "GET", "POST", "PUT", "DELETE", "PATCH" };

        private static IClaimsChecker _claimsChecker;
        public ClaimsCheckerAttribute(IClaimsChecker claimsChecker)
        {
            _claimsChecker = claimsChecker;
           
        }

        public ClaimsCheckerAttribute(string routePrefix)
        {
            _resource = routePrefix;
        }
        
        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            List<ResourceParameters> parameters = new List<ResourceParameters>();
            var owinContext = actionContext.Request.GetOwinContext();

            if (owinContext == null)
                throw new ArgumentException("No OWIN context found.");

            var claimsPrincipal =  actionContext.Request.GetOwinContext().Authentication.User;

            var bodyParameters = actionContext.Request.Content.ReadAsStringAsync().Result;

            JObject deserializedBodyParameters = new JObject();

            if (!string.IsNullOrEmpty(bodyParameters))
                deserializedBodyParameters = JObject.Parse(bodyParameters);

            if (claimsPrincipal == null || !claimsPrincipal.Identity.IsAuthenticated)
                return false;
            
            //get the parameters from query string
            var queryString = actionContext.Request.GetQueryNameValuePairs().ToList();

            foreach (KeyValuePair<string,string> value in queryString)
            {
                parameters.Add(new ResourceParameters() { Key = value.Key , Value = value.Value });
            }

            if (deserializedBodyParameters != null)
            {
                foreach (KeyValuePair<string,JToken> value in deserializedBodyParameters)
                {
                    parameters.Add(new ResourceParameters() { Key = value.Key, Value = value.Value.ToString() });
                }
            }

            //get the verb from the current context
            var verb = actionContext.Request.Method.Method;

            // check if the class is implemented using REST standards
            if (!_restVerbs.Any(rest => rest.ToUpperInvariant() == verb.ToUpperInvariant()))
                return false;

            //validate
            return Validate(claimsPrincipal, _resource, verb.ToUpperInvariant(), parameters);
        }

        public bool Validate(ClaimsPrincipal claimsPrincipal, string resource, string verb, List<ResourceParameters> parameters)
        {
            if (_claimsChecker == null)
                _claimsChecker = new ClaimsChecker();

            return _claimsChecker.Validate(claimsPrincipal, resource, verb, parameters);
        }
    }
}
